package com.zcy.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.zcy.service.IDyService;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by join on 2017/6/28.
 */
public abstract class DyServiceAbs implements IDyService{

    @PersistenceContext
    private EntityManager em;

    public abstract String getEntityName();

    @Override
    @Transactional
    public Map<String, Object> save(Map<String, Object> entity) {
        Session session = em.unwrap(Session.class);
        session.save(getEntityName(),entity);
        return entity;
    }

    @Override
    @Transactional
    public List<Map<String, Object>> save(List<Map<String, Object>> entitys) {
        Session session = em.unwrap(Session.class);
        for(Map<String, Object> entity : entitys){
            session.save(getEntityName(),entity);
        }
        return entitys;
    }

    @Override
    @Transactional
    public void delete(long id) {
        Session session = em.unwrap(Session.class);
        Object delObj = session.get(getEntityName(),id);
        session.delete(delObj);
    }

    @Override
    @Transactional
    public void delete(long[] ids) {
        Session session = em.unwrap(Session.class);
        for(long id : ids){
            delete(id);
        }
    }

    @Override
    @Transactional
    public void delete(String whereJson) {
        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(getEntityName());
        //criteria.add(Restrictions.eq("",""));
        criteria = buildQueryParam(whereJson,criteria);
        List li = criteria.list();
        li.forEach(item -> {
            session.delete(getEntityName(),item);
        });
    }

    @Override
    @Transactional
    public Map<String, Object> update(Map<String, Object> entity) {
        Session session = em.unwrap(Session.class);
        session.update(getEntityName(),entity);
        return entity;
    }

    @Override
    @Transactional
    public List<Map<String, Object>> update(List<Map<String, Object>> entitys) {
        Session session = em.unwrap(Session.class);
        entitys.forEach(entity -> {
            session.update(getEntityName(),entity);
        });
        return entitys;
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> findById(long id) {
        Session session = em.unwrap(Session.class);
        Object rs = session.get(getEntityName(),id);
        if(rs == null){
            return null;
        }else{
            return (Map<String, Object>)rs;
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> findOne(String queryStr) {
        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(getEntityName());
        //criteria.add(Restrictions.eq("",""));
        criteria = buildQueryParam(queryStr,criteria);
        return (Map<String, Object>)criteria.uniqueResult();
    }

    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> findAll(String queryStr) {
        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(getEntityName());
        //criteria.add(Restrictions.eq("",""));
        criteria = buildQueryParam(queryStr,criteria);
        return (List<Map<String, Object>>) criteria.list();
    }

    @Override
    @Transactional(readOnly = true)
    public Page<Map<String, Object>> findAll(String queryStr, Pageable pageable) {
        Session session = em.unwrap(Session.class);

        session.createQuery("").list();
        Criteria criteria = session.createCriteria(getEntityName());
        criteria = buildQueryParam(queryStr,criteria);
        Criteria finalCriteria = criteria;
        pageable.getSort().forEach(order -> {
            if(order.isAscending()){
                finalCriteria.addOrder(Order.asc(order.getProperty()));
            }else{
                finalCriteria.addOrder(Order.desc(order.getProperty()));
            }
        });
        criteria.setFirstResult(pageable.getOffset());
        criteria.setMaxResults(pageable.getPageSize());
        List<Map<String, Object>> rs = (List<Map<String, Object>>)criteria.list();
        Page<Map<String, Object>> page = new PageImpl<Map<String, Object>>(rs,pageable,rs.size());

        return page;
    }

    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> findAll(String queryStr, Sort sort) {
        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(getEntityName());
        criteria = buildQueryParam(queryStr,criteria);
        Criteria finalCriteria = criteria;
        sort.forEach(order -> {
            if(order.isAscending()){
                finalCriteria.addOrder(Order.asc(order.getProperty()));
            }else{
                finalCriteria.addOrder(Order.desc(order.getProperty()));
            }
        });
        List<Map<String, Object>> rs = (List<Map<String, Object>>)criteria.list();
        return rs;
    }

    @Override
    @Transactional(readOnly = true)
    public long count(String queryStr) {
        Session session = em.unwrap(Session.class);
        Criteria criteria = session.createCriteria(getEntityName());
        criteria = buildQueryParam(queryStr,criteria);

        criteria.setProjection(Projections.rowCount());
        Object rs = criteria.uniqueResult();
        if(rs == null){
            return 0;
        }else{
            return Long.parseLong(rs.toString());
        }
    }

    //#######################################################################################################
    //######################################     原生HQL查询   ############################################
    //#######################################################################################################

    @Override
    @Transactional(readOnly = true)
    public List<Map<String,Object>> findAllByHql(String hql){
        return findAllByHql(hql,new Object[]{});
    }

    @Override
    @Transactional(readOnly = true)
    public List<Map<String,Object>> findAllByHql(String hql,Object... params){
        Session session = em.unwrap(Session.class);
        Query query = session.createQuery(hql);
        if(params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i,params[i]);
            }
        }
        Object rs = query.list();
        if(rs == null){
            return new ArrayList<>();
        }else{
            return (List<Map<String,Object>>)rs;
        }
    }

    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> findAllByHql(String hql, Pageable pageable) {
        return findAllByHql(hql,pageable,null);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Map<String, Object>> findAllByHql(String hql, Pageable pageable, Object... params) {
        Session session = em.unwrap(Session.class);
        Query query = session.createQuery(hql);

        if(pageable != null) {
            query.setFirstResult(pageable.getOffset());
            query.setMaxResults(pageable.getOffset() + pageable.getPageSize());
        }

        if(params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i,params[i]);
            }
        }

        Object rs = query.list();
        if(rs == null){
            return new ArrayList<>();
        }else{
            return (List<Map<String,Object>>)rs;
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Object findHqlToObject(String hql){
        return findHqlToObject(null);
    }

    @Override
    @Transactional(readOnly = true)
    public Object findHqlToObject(String hql,Object... params){
        Session session = em.unwrap(Session.class);
        Query query = session.createQuery(hql);
        if(params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i,params[i]);
            }
        }
        return query.list();
    }

    //#######################################################################################################
    //######################################     原生sql查询   ############################################
    //#######################################################################################################

    @Override
    @Transactional(readOnly = true)
    public List<Map<String,Object>> findAllBySql(String sql){
        Session session = em.unwrap(Session.class);
        Query query = session.createSQLQuery(sql);
        query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        Object rs = query.list();
        if(rs == null){
            return new ArrayList<>();
        }else{
            return (List<Map<String,Object>>)rs;
        }
    }


    @Override
    @Transactional(readOnly = true)
    public List<Map<String,Object>> findAllBySql(String sql,Object... params){
        Session session = em.unwrap(Session.class);
        Query query = session.createSQLQuery(sql);
        if(params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i,params[i]);
            }
        }
        Object rs = query.list();
        if(rs == null){
            return new ArrayList<>();
        }else{
            return (List<Map<String,Object>>)rs;
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Page<Map<String,Object>> findAllBySql(String sql,Pageable pageable){
        return findAllBySql(sql,pageable,null);
    }

    @Override
    @Transactional(readOnly = true)
    public Page<Map<String, Object>> findAllBySql(String sql, Pageable pageable, Object... params) {

        Session session = em.unwrap(Session.class);
        Query query = session.createSQLQuery(sql);
        if(pageable != null) {
            query.setFirstResult(pageable.getOffset());
            query.setMaxResults(pageable.getOffset() + pageable.getPageSize());
        }

        if(params != null && params.length > 0) {
            for (int i = 0; i < params.length; i++) {
                query.setParameter(i,params[i]);
            }
        }

        Object rs = query.list();
        if(rs == null){
            return new PageImpl<Map<String,Object>>(new ArrayList<>(),pageable,0);
        }else{
            List<Map<String,Object>> rsLi = (List<Map<String,Object>>) rs;
            Page<Map<String, Object>> page = new PageImpl<Map<String, Object>>(rsLi,pageable,rsLi.size());
            return page;
        }
    }




    /**
     * 根据查询条件json字符串，封装动态查询对象
     * @Comments
     * name="zhangsan" AND age < 60 AND (stat in (0,1,2) or userId is null)
     * {
     *     and:{
     *         Eq_name:"zhangsan",
     *         Lt_age:60,
     *         or:{
     *              Eq_stat:[0,1,2],
     *              IsNull_userId:""
     *         }
     *     }
     * }
     * Eq
     * Bn(Between)
     * Lt(LessThan)
     * Gt(GreaterThan)
     * IsNull
     * IsNotNull
     * NotNull
     * Like
     * NotLike
     * Not
     * In
     * NotIn
     * @param queryJson
     * @param criteria
     * @return
     */
    protected Criteria buildQueryParam(String queryJson,Criteria criteria){
        JSONObject jsonObject = JSON.parseObject(queryJson, Feature.OrderedField);
        jsonObject.keySet().forEach(key -> {

            //"EQ_name_OR_loginName":"zhangsan"
            //"EQ_name_OR_loginName":"zhangsan"

        });
        return criteria;

    }




}
